// 信号处理
//kill -INT pid 终止
//kill -TERM pid 重启
//需要调用Wg.Add()
//需要监听Shutdown通道

//go:build !windows
// +build !windows

package signal

import (
	"context"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strconv"
	"syscall"
	"time"

	"gitee.com/satyr/tools/logger"
)

type signalContext struct {
	//Shutdown 业务方手动监听此通道获知通知
	Ctx    context.Context    `json:"-"`
	Cancel context.CancelFunc `json:"-"`
}

var (
	signalCtx   *signalContext
	shutdownCtx *signalContext
)

func Init() {
	signalCtx = &signalContext{}
	signalCtx.Ctx, signalCtx.Cancel = context.WithCancel(context.Background())

	shutdownCtx = &signalContext{}
	shutdownCtx.Ctx, shutdownCtx.Cancel = context.WithCancel(context.Background())

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT)
	defer signal.Stop(c)
	s := <-c
	switch s {
	case syscall.SIGUSR2, syscall.SIGHUP:
		//fmt.Println("receive SIGUSR2/SIGHUP signal...")
		err := restart()
		if err != nil {
			logger.Errorf("Restart: Failed to launch, error: %v", err)
			//logger.Fatalf("Restart: Failed to launch, error: %v", err)
		}
	case syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT:
		//fmt.Println("receive TERM/SIGINT/SIGQUIT signal...")
		exit()
	}
}

func restart() error {
	signalCtx.Cancel()
	logger.Warn("restart application...")
	<-time.After(time.Second)
	var err error

	var name string
	name, err = exec.LookPath(os.Args[0])
	if err != nil {
		logger.Fatalf("error looking path: %v", err)
		return err
	}

	env := os.Environ()

	var args []string
	if len(os.Args) > 1 {
		args = os.Args[1:]
	}

	var dir string
	dir, err = os.Getwd()

	if err != nil {
		logger.Fatalf("error getting getwd user: %v", err)
	}

	u, err := user.Current()
	if err != nil {
		logger.Fatalf("error getting current user: %v", err)
		return err
	}

	uid, err := strconv.Atoi(u.Uid)
	if err != nil {
		logger.Fatalf("error converting Uid=%s to integer: %v", u.Uid, err)
		return err
	}

	gid, err := strconv.Atoi(u.Gid)
	if err != nil {
		logger.Fatalf("error converting Gid=%s to integer: %v", u.Gid, err)
		return err
	}

	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.Path = name
	cmd.Dir = dir
	cmd.Env = env
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Credential: &syscall.Credential{
			Uid:         uint32(uid),
			Gid:         uint32(gid),
			NoSetGroups: true,
		},
	}
	err = cmd.Start()
	if err != nil {
		logger.Warn(err)
		return err
	}
	shutdownCtx.Cancel()
	return nil
}

func exit() {
	signalCtx.Cancel()
	//logger.Warn("exit application...")
	<-time.After(time.Second)
	shutdownCtx.Cancel()
}

//GetSignalContext 一般用于其他包或者非http程序
func GetSignalContext() *signalContext {
	return signalCtx
}

func GetShutdownContext() *signalContext {
	return shutdownCtx
}
